/*! \file MESSAGE_QUEUE.h
  \brief Une classe template permettant de construire des files de messages pour faire de l'ipc.
 * \author Ameziane NAIT ABDELAZIZ
*/



#ifndef MESSAGE_QUEUE_H
#define	MESSAGE_QUEUE_H

#include <cassert>
#include <sys/msg.h>
#include <stddef.h>
#include <cstdio>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <errno.h>
/*-------------------MESSAGE QUEUES FUNCTIONS-------------------------*/

#define FLAG 0
#define SLEEP sleep(2)

/**
 * \brief message tag pour echanger des messages entre deux processus
 */
enum MESSAGE_TAG
{
    MSG_TAG_ALL=0,
    MSG_TAG_RECU=1,
    MSG_TAG_QUIT=2,
    MSG_TAG_PLEIN=3,
};


/**
 * \brief identifiant de file de message
 */
enum QUEUES_ID{
    QID_1=0,
    QID_2=1,
    QID_3=2,
    QID_4=3,
};


/**
 * \brief structure de données message
 */
typedef struct {
    /**
     * identifiant du message , equivalent MPI du message tag
     */
    long mtype;
    
    /**
     * contenu du message
     */
    long events_count;
} message_t;

/**
 * \class MESSAGE_QUEUE
 * \brief Cette classe permet d'envoyer des messages entre processus
 */
template <QUEUES_ID queueID> class MESSAGE_QUEUE{
    
private:
    /**
     * \brief clef 1 pour file de message 1
     */
    static key_t key_12;
    
    /**
     * \brief identifiant de la file de message sens 1->2
     */
    static int msqid_12;

    /**
     * \brief clef 2 pour file de message 2
     */
    static key_t key_21;
    
    /**
     * \brief identifiant de la file de message sens 2->1 
     */
    static int msqid_21; 
    
    /**
     * \brief instance de message que s'echangent les processus 
     */
    static message_t msg;
    
private:
    /**
     * \brief genere une clef pour une message queue à partir d'un fichier
     */
    static void makeProducerKey(const char* path);
    /**
     * 
     * @param path
     */
    static void makeConsumerKey(const char* path);
    
public:    
    /**
     * 
     * @param tag
     * @return 
     */
    static int  sendMessageToProducer(MESSAGE_TAG tag);
    /**
     * 
     * @param msg
     * @param tag
     * @return 
     */
    static long  receiveMessageFromConsumer(MESSAGE_TAG tag);
    
    /**
     * 
     * @param tag
     * @return 
     */
    static int  sendMessageToConsumer(MESSAGE_TAG tag,long events_count);
    
    
    /**
     * 
     * @return 
     */
    static long getEventsCount();
    
    /**
     * 
     * @param msg
     * @param tag
     * @return 
     */
    static long  receiveMessageFromProducer(MESSAGE_TAG tag);
    
    /**
     * ferme les files d'attentes
     */
    static void destroyQueues();
    
    /**
     * 
     */
    static void initQueues();
};



/*----------IMPELMENTATION DES MESSAGE QUEUES-------------------------*/
template <QUEUES_ID queueID> key_t       MESSAGE_QUEUE<queueID>::key_12;
template <QUEUES_ID queueID> int         MESSAGE_QUEUE<queueID>::msqid_12;
template <QUEUES_ID queueID> key_t       MESSAGE_QUEUE<queueID>::key_21;
template <QUEUES_ID queueID> int         MESSAGE_QUEUE<queueID>::msqid_21; 
template <QUEUES_ID queueID> message_t   MESSAGE_QUEUE<queueID>::msg;



template <QUEUES_ID queueID> void  MESSAGE_QUEUE<queueID>::makeProducerKey(const char* path){
        assert((key_12 = ftok(path,'B'))!=-1);
        assert((msqid_12 = msgget(key_12,0644 | IPC_CREAT))!=-1);
}
    
template <QUEUES_ID queueID> void MESSAGE_QUEUE<queueID>::makeConsumerKey(const char* path){
        assert((key_21 = ftok(path,'B'))!=-1);
        assert((msqid_21 = msgget(key_21,0644 | IPC_CREAT))!=-1);
}


template <QUEUES_ID queueID> int  MESSAGE_QUEUE<queueID>::sendMessageToProducer(MESSAGE_TAG tag){
        msg.mtype=tag;
        if(msgsnd(msqid_21,&msg,sizeof(message_t)-sizeof(long),FLAG)==-1){
              SLEEP;
              fprintf(stderr,"echec de l'envoie erreur : %d =>msg type :%li\n",errno,msg.mtype);
              return -1;
        };
        
        return 0;
}

template <QUEUES_ID queueID> long  MESSAGE_QUEUE<queueID>::receiveMessageFromConsumer(MESSAGE_TAG tag){
  if(msgrcv(msqid_21,&msg,sizeof(message_t)-sizeof(long),tag,FLAG)==-1){
    SLEEP;
    fprintf(stderr,"echec de la reception : %d msg type :%ld\n",errno,msg.mtype);
    return -1;
  };
  return msg.mtype;
}

template <QUEUES_ID queueID> int  MESSAGE_QUEUE<queueID>::sendMessageToConsumer(MESSAGE_TAG tag,long events_count){
        
        msg.mtype=tag;
        msg.events_count=events_count;
        if(msgsnd(msqid_12,&msg,sizeof(message_t)-sizeof(long),FLAG)==-1){
           fprintf(stderr,"sendMessageToConsumer::error");
           return -1;
         } ;
         
         return 0;
    }
    
    

template <QUEUES_ID queueID> long  MESSAGE_QUEUE<queueID>::receiveMessageFromProducer(MESSAGE_TAG tag){
        if(msgrcv(msqid_12,&msg,sizeof(message_t)-sizeof(long),tag,FLAG)==-1){
                //printf("echec de la reception\n");
            };
            //printf("recu du producteur %d evenements\n",msg.events_count);
            return msg.mtype;
}

template <QUEUES_ID queueID> void MESSAGE_QUEUE<queueID>::destroyQueues(){
        //printf("fin des files\n");
        assert(msgctl(msqid_12,IPC_RMID,NULL)!=-1);
        assert(msgctl(msqid_21,IPC_RMID,NULL)!=-1);
  };

  
template <QUEUES_ID queueID> void MESSAGE_QUEUE<queueID>::initQueues(){
    makeConsumerKey("../");
    makeProducerKey(".");
}


template <QUEUES_ID queueID> long MESSAGE_QUEUE<queueID>::getEventsCount(){
    
    return msg.events_count;
}
#endif	/* MESSAGE_QUEUES_H */

